package com.google.code.kjug.guava;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.junit.Test;

import java.util.*;

import static junit.framework.Assert.*;

public class JavaCollections_UT {


    @Test
    public void aListIsJustLikeAnArray() {
        String[] arrayOfStrings = new String[] {"alpha", "beta", "gamma", "delta"};
        List<String> collectionOfStrings = Arrays.asList("alpha", "beta", "gamma", "delta");

        assertEquals(arrayOfStrings.length, collectionOfStrings.size());
        assertEquals(arrayOfStrings[0], collectionOfStrings.get(0));
        assertEquals(arrayOfStrings[1], collectionOfStrings.get(1));
        assertEquals(arrayOfStrings[2], collectionOfStrings.get(2));
        assertEquals(arrayOfStrings[3], collectionOfStrings.get(3));
    }

    @Test
    public void aCollectionIsJustLikeAnArray() {
        String[] arrayOfStrings = new String[] {"alpha", "beta", "gamma", "delta"};
        Collection<String> collectionOfStrings = Arrays.asList("alpha", "beta", "gamma", "delta");

        Iterator<String> iterator = collectionOfStrings.iterator();
        assertEquals(arrayOfStrings.length, collectionOfStrings.size());
        assertEquals(arrayOfStrings[0], iterator.next());
        assertEquals(arrayOfStrings[1], iterator.next());
        assertEquals(arrayOfStrings[2], iterator.next());
        assertEquals(arrayOfStrings[3], iterator.next());
    }

    @Test
    public void aSetIsNotLikeAnArray() {
        String[] arrayOfStrings = new String[] {"alpha", "alpha", "alpha", "alpha"};
        Set<String> setOfStrings = Sets.newHashSet("alpha", "alpha", "alpha", "alpha");

        assertEquals(4, arrayOfStrings.length);
        assertEquals(1, setOfStrings.size());
        assertEquals(arrayOfStrings[0], setOfStrings.iterator().next());  //notice there is not get with Sets!!!!
    }

    @Test
    public void aMapIsDefinitelyNotLikeAnArray() {
        String[] arrayOfStrings = new String[] {"alpha", "beta", "gamma", "delta"};
        Map<String, String> mapOfStrings = Maps.newHashMap();
        mapOfStrings.put("first letter", "alpha");
        mapOfStrings.put("second letter", "beta");
        mapOfStrings.put("third letter", "gamma");
        mapOfStrings.put("fourth letter", "delta");

        assertEquals(arrayOfStrings.length, mapOfStrings.size());
        assertEquals(arrayOfStrings[0], mapOfStrings.get("first letter"));  //notice no iterator for Maps!!!
    }

    @Test
    public void heyWhatAboutTheseVectorThings() {
        //vectors are just like arraylists except thread safe
        Vector<String> vectorOfStrings = new Vector<String>();
        vectorOfStrings.add("alpha");
        vectorOfStrings.add("beta");
        vectorOfStrings.add("gamma");
        vectorOfStrings.add("delta");

        assertEquals(4, vectorOfStrings.size());
        assertEquals("alpha", vectorOfStrings.get(0));
        assertEquals("beta", vectorOfStrings.get(1));
        assertEquals("gamma", vectorOfStrings.get(2));
        assertEquals("delta", vectorOfStrings.get(3));
    }

    @Test
    public void canIturnMyArrayIntoAList() {
        //Sure!!!
        String[] arrayOfStrings = new String[] {"alpha", "alpha", "alpha", "alpha"};
        List<String> listOfStrings = Lists.newArrayList();

        Collections.addAll(listOfStrings, arrayOfStrings);

        assertEquals(arrayOfStrings.length, listOfStrings.size());
        assertEquals(arrayOfStrings[0], listOfStrings.get(0));
        assertEquals(arrayOfStrings[1], listOfStrings.get(1));
        assertEquals(arrayOfStrings[2], listOfStrings.get(2));
        assertEquals(arrayOfStrings[3], listOfStrings.get(3));
    }

    @Test
    public void canIturnMyArrayIntoAListInJustOneLine() {

    }

    @Test
    public void canIRemoveStuffFromMyList() {
        List<String> listOfStrings = Lists.newArrayList("alpha", "beta", "gamma", "delta");

        listOfStrings.remove("alpha");

        assertEquals("[beta, gamma, delta]", listOfStrings.toString());
    }

    @Test (expected = ConcurrentModificationException.class)
    @Gotcha
    public void canIRemoveStuffThisWay() {
        //nope.  no way!
        List<Integer> listOfNumbers = Lists.newArrayList(1, 2, 3, 4, 5);

        for (Integer currentNumber : listOfNumbers) {
            if (currentNumber > 2) {
                listOfNumbers.remove(currentNumber);
            }
        }

        //exception expected
    }

    @Test
    public void ohSoThisIsTheWayItShouldBeRemoved() {
        List<Integer> listOfNumbers = Lists.newArrayList(1, 2, 3, 4, 5);

        Iterator<Integer> iterator = listOfNumbers.iterator();
        while(iterator.hasNext()) {
            Integer next = iterator.next();
            if (next > 2) {
                iterator.remove(); // have to use the iterator to remove, not the list
            }
        }

        assertEquals(2, listOfNumbers.size());
        assertEquals(1, listOfNumbers.get(0).intValue());
        assertEquals(2, listOfNumbers.get(1).intValue());
    }

    @Test
    public void canIHaveListsOfMyVeryOwnCustomObjects() {
        Air justSomeObject = new Air();
        Air differentObject = new Air();

        List<Air> listOfClasses = Lists.newArrayList(justSomeObject, differentObject);

        assertEquals(2, listOfClasses.size());
        assertEquals(justSomeObject, listOfClasses.get(0));
        assertEquals(differentObject, listOfClasses.get(1));
    }
    private class Air {}

    @Test
    public void whySetTheStartingSizeOfMyList() {
        List<String> strings = new ArrayList<String>(10000);  //affects performance
        strings.add("a");
        strings.add("b");
        strings.add("c");

        int size = strings.size();

        assertEquals(3, size);
    }

    @Test
    @Gotcha
    public void mapsDontHaveExplicitTypeChecking() {
        Map<Integer, String> mapOfStrings = new HashMap<Integer, String>();
        mapOfStrings.put(1, "a");
        mapOfStrings.put(2, "b");

        String firstString = mapOfStrings.get(new Long(1));

        assertNull(firstString);
        assertEquals("a", mapOfStrings.get(1));
    }

    @Test
    @Gotcha
    public void collectionsAlsoDontHaveTypeChecking() {
        Collection<Integer> collectionOfStrings = new ArrayList<Integer>();
        collectionOfStrings.add(1);
        collectionOfStrings.add(2);

        boolean contains = collectionOfStrings.contains(1L);

        assertFalse(contains);
        assertTrue(collectionOfStrings.contains(1));
    }

    @Test
    @Gotcha
    public void collectionsHoldRealObjectsNotJustCopies() {
        ClassWithAttribute object = new ClassWithAttribute();
        object.setAttribute("alpha");
        Collection<ClassWithAttribute> collectionOfObjects = new ArrayList<ClassWithAttribute>();
        collectionOfObjects.add(object);

        object.setAttribute("beta");

        assertEquals("beta", collectionOfObjects.iterator().next().getAttribute());
    }

    private class ClassWithAttribute {
        private String attribute;

        public String getAttribute() {
            return attribute;
        }

        public void setAttribute(String attribute) {
            this.attribute = attribute;
        }
    }
}
